package testrank.predictors;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import testrank.Conflict;
import testrank.Predictor;
import testrank.TestRankConstants;

/**
 * @author Hagai Cibulski
 */
public class StackDepthExecutionCountPredictor implements Predictor {

	private static final long serialVersionUID = 1L;

	/**
	 * conflict[function][test]
	 */
	private Map<Conflict, Double> conflicts = new HashMap<Conflict, Double>();
	
	protected Map<String, Double> totalExecutionCountDuringTest = new HashMap<String, Double>();

	
	private SortedSet<String> allTests = new TreeSet<String>(); 
	
	private transient int depth = 0;
	
	@Override
	public double rankFailure(String methodSignature, String testSignature) {
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Double count = conflicts.get(conflict);

		if (TestRankConstants.USE_ALT_NORMALIZATION) {
			// normalize the conflict count by the total execution count of the test
			Double total = totalExecutionCountDuringTest.get(testSignature);
			double rank = (count == null) ? 0 : count / total;
			return rank;
		}
		else {
			double rank = (count == null) ? 0 : (double)count / (count + 1.0);
			return rank;
		}
	}

	@Override
	public void begin(String methodSignature, String testSignature) {
		depth++;
		
		allTests.add(testSignature);
		
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Double count = conflicts.get(conflict);
		if (count == null) {
			count = 0.0;
		}
		
		int stackDepth = depth;//Thread.currentThread().getStackTrace().length;
		double depthRank = 1.0 / stackDepth;  
			
		count += depthRank;
		conflicts.put(conflict, count);
		
		// update total execution count of methods in test
		Double total = totalExecutionCountDuringTest.get(testSignature);
		if (total == null) {
			total = 0.0;
		}
		total += depthRank;
		totalExecutionCountDuringTest.put(testSignature, total);
	}

	@Override
	public void end(String methodSignature, String testSignature) {
		depth--;
	}

	@Override
	public String toString() {
		return  getName() + ":" + conflicts.toString().replace(", Conflict", ",\nConflict") + "\nTotal Conflicts = " + conflicts.size();
	}
	
	public void printConflicts() {
		System.out.println(toString());
	}

	public Set<Conflict> getAllConflicts() {
		return conflicts.keySet();
	}
	
	public double rankFailureConverge(String methodSignature, String testSignature) {
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Double count = conflicts.get(conflict);
		if (count == null)
			return 0;
		return (double)count / (count + 1.0);
	}
	
	@Override
	public String getName() {
		return "STKEXCNT";
	}
}

